package com.zlx.redismessage.util;

import com.zlx.redismessage.task.ScheduleTask;
import org.apache.commons.lang3.StringUtils;
import org.quartz.CronExpression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.Assert;

import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;

/**
 * 定时任务工具类
 */
public class ScheduleUtils {

    private static final Logger logger = LoggerFactory.getLogger(ScheduleUtils.class);

    private static final ThreadPoolTaskScheduler threadPoolTaskScheduler;
    private static final Map<String, ScheduledFuture<?>> scheduledFutureMap = new HashMap<>();

    static {
        threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.afterPropertiesSet();
        // 设置最大线程数, 默认值为1
        threadPoolTaskScheduler.setPoolSize(1);
        // 设置线程前缀
        threadPoolTaskScheduler.setThreadNamePrefix("detection-task-");
        // 设置取消策略
        threadPoolTaskScheduler.setRemoveOnCancelPolicy(true);
        // 设置关机时等待任务完成
        threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        // 设置等待任务完成的最大时间单位/秒
        threadPoolTaskScheduler.setAwaitTerminationSeconds(10);
        // 设置异常处理策略
        threadPoolTaskScheduler.setErrorHandler((throwable) -> {
            logger.error("检测任务开启失败", throwable);
        });
        threadPoolTaskScheduler.initialize();
        logger.info("初始化线程池...");
    }

    /**
     * 获取线程池
     *
     * @return
     */
    public static ThreadPoolTaskScheduler getThreadPoolTaskScheduler() {
        return threadPoolTaskScheduler;
    }

    /**
     * 根据cron表达式开启定时任务
     *
     * @param scheduleTask 具体任务
     * @param cron         cron表达式
     */
    public static void start(ScheduleTask scheduleTask, String cron) {
        if (isExist(scheduleTask.getId())) {
            logger.info("启动定时任务" + scheduleTask.getId() + "失败，任务已存在");
            return;
        }
        boolean validExpression = CronExpression.isValidExpression(cron);
        Assert.isTrue(validExpression, "违法的Cron表达式");
        threadPoolTaskScheduler.schedule(scheduleTask, new CronTrigger(cron));
    }

    /**
     * 启动某定时任务，到时间点就运行一次
     *
     * @param scheduleTask
     * @param startTime
     */
    public static void start(ScheduleTask scheduleTask, Date startTime) {
        long initialDelay = startTime.getTime() - System.currentTimeMillis();
        if (initialDelay < 0L) {
            logger.warn("任务已过期!");
            return;
        }

        String id = scheduleTask.getId();
        if (StringUtils.isNotBlank(id)) {
            if (isExist(scheduleTask.getId())) {
                logger.info("启动定时任务" + scheduleTask.getId() + "失败，任务已存在");
                return;
            }
        }

        Object param = scheduleTask.getParam();
        if (null != param) {
            if (isExist(scheduleTask.getParam().toString())) {
                logger.info("启动定时任务" + scheduleTask.getParam().toString() + "失败，任务已存在");
                return;
            }
        }
        ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(scheduleTask, startTime);
        if (StringUtils.isNotBlank(scheduleTask.getId())) {
            scheduledFutureMap.put(scheduleTask.getId(), schedule);
        } else {
            scheduledFutureMap.put(scheduleTask.getParam().toString(), schedule);
        }
        logger.info("启动定时任务" + scheduleTask.getId() + "，执行时间为" + startTime);

    }

    /**
     * 启动某定时任务，以固定周期运行
     *
     * @param scheduleTask
     * @param period
     */
    public static void start(ScheduleTask scheduleTask, long period) {
        if (isExist(scheduleTask.getId())) {
            logger.info("启动定时任务" + scheduleTask.getId() + "失败，任务已存在");
            return;
        }
        ScheduledFuture<?> future = threadPoolTaskScheduler.scheduleAtFixedRate(scheduleTask, new Date(), period);
        scheduledFutureMap.put(scheduleTask.getId(), future);
        logger.info("启动定时任务" + scheduleTask.getId() + "，执行周期为" + period + "毫秒");
    }

    /**
     * 取消某定时任务
     *
     * @param taskId    指定任务id
     * @param waitTimes 取消任务等待时间 单位/秒
     */
    public static void cancel(String taskId, Long waitTimes) throws InterruptedException {
        ScheduledFuture<?> future = scheduledFutureMap.get(taskId);
        if (null != future) {
            if (future.cancel(true)) {
                scheduledFutureMap.remove(taskId);
                logger.info("取消定时任务{}", taskId);
            } else {
                logger.debug("取消定时任务{}失败。", taskId);
            }
        }
    }

    /**
     * 修改定时任务执行时间
     *
     * @param scheduleTask
     * @param startTime
     */
    public static void reset(ScheduleTask scheduleTask, Date startTime) {
        //先取消定时任务
        String id = scheduleTask.getId();
        ScheduledFuture<?> future = scheduledFutureMap.get(id);
        if (future != null) {
            if (future.cancel(false)) {
                scheduledFutureMap.remove(id);
                //然后启动新的定时任务
                ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(scheduleTask, startTime);
                scheduledFutureMap.put(id, schedule);
                logger.info("重置定时任务:{}，执行时间为:{}", id, startTime);
                return;
            }
        }
        logger.error("任务重置失败。");
    }

    /**
     * 判断某个定时任务是否存在或已经取消
     *
     * @param id
     */
    public static Boolean isExist(String id) {
        ScheduledFuture<?> future = scheduledFutureMap.get(id);
        if (future != null) {
            return true;
        }
        return false;
    }

    /**
     * 根据cron表达式,获取下一次执行时间
     *
     * @param cron
     * @return
     */
    public static Date getNextExecuteTime(String cron) throws ParseException {
        boolean validExpression = CronExpression.isValidExpression(cron);
        Assert.isTrue(validExpression, "违法的Cron表达式");
        CronExpression cronExpression = new CronExpression(cron);
        Date date = cronExpression.getTimeAfter(new Date());
        return date;
    }

    /**
     * 获取活跃的任务数量
     *
     * @return
     */
    public static int getActiveCount() {
        return threadPoolTaskScheduler.getActiveCount();
    }
}

